package control;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
import model.Move;
import view.ChessFrame;

/**
 * this class is in charge of the communication with the data base
 */
public class Communication {

    /**
     * constant string {@value}
     */
    private static final String U_NICK_EUQ = "uNick=";

    /**
     * constant string {@value}
     */
    private static final String AND_U_ID_EUQ = "&uID=";

    /**
     * constant string {@value}
     */
    private static final String AND_D_ID_EUQ = "&dID=";

    /**
     * constant string {@value}
     */
    private static final String AND_MOVE_MATCH_ID_EUQ = "&moveMatchID=";

    /**
     * constant string {@value}
     */
    private static final String ENCODE_UTF8 = "UTF-8";

    /**
     * constant string {@value}
     */
    private static final String S = "S";

    /**
     * constant string {@value}
     */
    private static final String F = "F";

    /**
     * constant string {@value}
     */
    private static final String SEP = "#";

    /**
     * constant string {@value}
     */
    private static final String ZERO = "0";

    /**
     * constant string {@value}
     */
    private static final String ONE = "1";

    /**
     * checks the login data, on success the user will be logged in
     * 
     * @param name
     *            nickname
     * @param pw
     *            password
     * @return on success the user data, on fail an error message
     */
    public static String login( String name, String pw )
    {
	ChessFrame.FRAME_LOCK = true;
	String ret = "F#LOGIN";
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + "&uPw="
		    + URLEncoder.encode( convertMD5( pw.trim() ), ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/login.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = line;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "login error" );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	catch ( NoSuchAlgorithmException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret;
    }

    /**
     * list all actual matches with the logged in user
     * 
     * @param id
     *            id of the user
     * @param name
     *            name of the user
     * @return array with data of the actual matches
     */
    // S# \n mID#NickOne#NickTwo#Startdate#movecount
    public static String[] getActMatches( int id, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	LinkedList<String> ret = new LinkedList<String>();
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_U_ID_EUQ
		    + URLEncoder.encode( "" + id, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/actualMatches.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line = reader.readLine();
	    if ( line != null && line.split( SEP )[0].equalsIgnoreCase( S ) )
	    {
		while ( ( line = reader.readLine() ) != null )
		{
		    ret.add( line );
		}
	    }
	    else if ( line != null && line.split( SEP )[0].equalsIgnoreCase( F ) )
	    {
		System.out.println( "getActMatches error" );
		// TODO: proper error handling =D
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}

	ChessFrame.FRAME_LOCK = false;
	return ret.toArray( new String[ret.size()] );
    }

    /**
     * list all archived matches with the logged in user
     * 
     * @param id
     *            id of the user
     * @param name
     *            name of the user
     * @return array with data of the archived matches
     */
    // S# \n mID#NickOne#NickTwo#WinnerNick#Startdate#movecount#remis#patt
    public static String[] getArchMatches( int id, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	LinkedList<String> ret = new LinkedList<String>();
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_U_ID_EUQ
		    + URLEncoder.encode( "" + id, ENCODE_UTF8 );

	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/archMatches.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line = reader.readLine();
	    if ( line != null && line.split( SEP )[0].equalsIgnoreCase( S ) )
	    {
		while ( ( line = reader.readLine() ) != null )
		{
		    ret.add( line );
		}
	    }
	    else if ( line != null && line.split( SEP )[0].equalsIgnoreCase( F ) )
	    {
		System.out.println( "getArchMatches error" );
		// TODO: proper error handling =D
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}

	ChessFrame.FRAME_LOCK = false;
	return ret.toArray( new String[ret.size()] );
    }

    /**
     * gets the list of all user, except oneself
     * 
     * @param name
     *            name of the user
     * @return list of all user, except oneself
     */
    public static String getUserList( String name )
    {
	ChessFrame.FRAME_LOCK = true;
	String ret = "F#USER LIST";
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/userList.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = line;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "getUserList error" );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret;
    }

    /**
     * gets the list of all dares
     * 
     * @param id
     *            id of the user
     * @param name
     *            name of the user
     * @return list of all dares
     */
    public static String[] getOpenDares( int id, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	LinkedList<String> ret = new LinkedList<String>();
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_U_ID_EUQ
		    + URLEncoder.encode( "" + id, ENCODE_UTF8 );

	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/openDares.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line = reader.readLine();
	    if ( line != null && line.split( SEP )[0].equalsIgnoreCase( S ) )
	    {
		while ( ( line = reader.readLine() ) != null )
		{
		    if ( line.split( SEP ).length < 6 )
		    {
			ret.add( ret.removeLast() + "\n" + line );
		    }
		    else
		    {
			ret.add( line );
		    }
		}
	    }
	    else if ( line != null && line.split( SEP )[0].equalsIgnoreCase( F ) )
	    {
		System.out.println( "getOpenDares error" );
		// TODO: proper error handling =D
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret.toArray( new String[ret.size()] );
    }

    /**
     * sends a new dare
     * 
     * @param name
     *            name of the user
     * @param opponent
     *            name of the challenged user
     * @param color
     *            color the challenging user wants to play
     * @param msg
     *            the message from the challenging user
     * @return answer string of the php
     */
    public static String sendDare( String name, String opponent, boolean color, String msg )
    {
	ChessFrame.FRAME_LOCK = true;
	String ret = "F#SEND DARE";
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + "&uOpp="
		    + URLEncoder.encode( opponent, ENCODE_UTF8 ) + "&dColor=" + ( color ? ONE : ZERO ) + "&dMessage="
		    + msg;
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/createDare.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = line;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "sendDare error" );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret;
    }

    /**
     * accepts a dare and creates a new match
     * 
     * @param dID
     *            id of the user
     * @param name
     *            name of the user
     * @return answer string of the php
     */
    public static String acceptDare( int dID, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	String ret = "F#ACCEPT DARE";
	try
	{

	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_D_ID_EUQ
		    + URLEncoder.encode( "" + dID, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/acceptDare.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = line;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "acceptDare error" );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}

	ChessFrame.FRAME_LOCK = false;
	return ret;
    }

    /**
     * declines a dare
     * 
     * @param dID
     *            id of the user
     * @param name
     *            name of the user
     * @return answer string of the php
     */
    public static boolean declineDare( int dID, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	boolean ret = false;
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_D_ID_EUQ
		    + URLEncoder.encode( "" + dID, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/declineDare.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = true;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "declineDare error: " + line );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret;
    }

    /**
     * gets all moves with the given match id
     * 
     * @param matchID
     *            the given match id
     * @param name
     *            the name of the logged in user
     * @return array of the moves
     */
    public static String[] getActMoves( int matchID, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	List<String> ret = new LinkedList<String>();

	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_MOVE_MATCH_ID_EUQ
		    + URLEncoder.encode( "" + matchID, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/actualMoves.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line = reader.readLine();
	    if ( line != null && line.split( SEP )[0].equalsIgnoreCase( S ) )
	    {
		while ( ( line = reader.readLine() ) != null )
		{
		    ret.add( line );
		}
	    }
	    else if ( line != null && line.split( SEP )[0].equalsIgnoreCase( F ) )
	    {
		System.err.println( "getActMoves error: " + line );
		// TODO proper error handling
	    }
	    reader.close();

	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}

	ChessFrame.FRAME_LOCK = false;
	return ret.toArray( new String[ret.size()] );
    }

    /**
     * gets all moves with the given match id of the archived moves
     * 
     * @param matchID
     *            the given match id
     * @param name
     *            the name of the logged in user
     * @return array of the moves
     */
    public static String[] getArchMoves( int matchID, String name )
    {
	ChessFrame.FRAME_LOCK = true;
	List<String> ret = new LinkedList<String>();

	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + AND_MOVE_MATCH_ID_EUQ
		    + URLEncoder.encode( "" + matchID, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/archMoves.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line = reader.readLine();
	    if ( line != null && line.split( SEP )[0].equalsIgnoreCase( S ) )
	    {
		while ( ( line = reader.readLine() ) != null )
		{
		    ret.add( line );
		}
	    }
	    else if ( line != null && line.split( SEP )[0].equalsIgnoreCase( F ) )
	    {
		System.err.println( "getArchMoves error: " + line );
		// TODO proper error handling
	    }
	    reader.close();

	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}

	ChessFrame.FRAME_LOCK = false;
	return ret.toArray( new String[ret.size()] );
    }

    /**
     * sends a new move, increases the move count of its match
     * 
     * @param name
     *            the name of the logged in user
     * @param newMove
     *            the new move to insert in the database
     * @param matchID
     *            the match id of the move
     * @return answer string of the php
     */
    public static String sendMove( String name, Move newMove, int matchID )
    {
	ChessFrame.FRAME_LOCK = true;
	String ret = "F#SEND MOVE";
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + "&mNr="
		    + URLEncoder.encode( "" + newMove.getNumber(), ENCODE_UTF8 ) + "&mMatchID="
		    + URLEncoder.encode( "" + matchID, ENCODE_UTF8 ) + "&mFrom="
		    + URLEncoder.encode( newMove.getStartField(), ENCODE_UTF8 ) + "&mTo="
		    + URLEncoder.encode( newMove.getEndField(), ENCODE_UTF8 ) + "&mCastling="
		    + URLEncoder.encode( newMove.isCastling() ? ONE : ZERO, ENCODE_UTF8 ) + "&mChecked="
		    + URLEncoder.encode( newMove.isChecked() ? ONE : ZERO, ENCODE_UTF8 ) + "&mCheckmate="
		    + URLEncoder.encode( newMove.isCheckmate() ? ONE : ZERO, ENCODE_UTF8 ) + "&mEnPassant="
		    + URLEncoder.encode( newMove.isEnPassant() ? ONE : ZERO, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/createMove.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = line;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "sendMove error: " + line.split( SEP )[1] );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret;

    }

    /**
     * transfers the move entries from moves_act to moves_arch for the given match and sets the winner, remis or patt in
     * the given match
     * 
     * @param name
     *            the name of the logged in user
     * @param matchID
     *            the match id of the finished match
     * @param matchResult
     *            the result of the finished match, use values 1 to 4,<br>
     *            1 - player one is the winner,<br>
     *            2 - player two is the winner,<br>
     *            3 - the match ended with remis,<br>
     *            4 - the match ended with patt
     * @return answer string of the php
     */
    public static String finishMatch( String name, int matchID, int matchResult )
    {
	ChessFrame.FRAME_LOCK = true;
	String ret = "F#FINISH MATCH";
	try
	{
	    String message = U_NICK_EUQ + URLEncoder.encode( name, ENCODE_UTF8 ) + "&matchID="
		    + URLEncoder.encode( "" + matchID, ENCODE_UTF8 ) + "&matchResult="
		    + URLEncoder.encode( "" + matchResult, ENCODE_UTF8 );
	    URL url = new URL( "http://coffeeandbits.de/corresChess/backend/finishMatch.php" );
	    HttpURLConnection urlConn = createURLConn( message, url );

	    // receive message
	    BufferedReader reader = new BufferedReader( new InputStreamReader( urlConn.getInputStream() ) );

	    String line;
	    while ( ( line = reader.readLine() ) != null )
	    {
		if ( line.split( SEP )[0].equalsIgnoreCase( S ) )
		{
		    ret = line;
		}
		else if ( line.split( SEP )[0].equalsIgnoreCase( F ) )
		{
		    System.out.println( "finishMatch error: " + line.split( SEP )[1] );
		    // TODO: proper error handling =D
		}
	    }
	    reader.close();
	}
	catch ( IOException e )
	{
	    e.printStackTrace();
	}
	ChessFrame.FRAME_LOCK = false;
	return ret;
    }

    /**
     * help method to convert the password in a hash value
     * 
     * @param str
     *            string to convert
     * @return 32 characters long hash value as string
     * @throws NoSuchAlgorithmException
     *             thrown when a NoSuchAlgorithmException occurs
     */
    private static String convertMD5( String str ) throws NoSuchAlgorithmException
    {
	/* calculation */
	MessageDigest md5 = MessageDigest.getInstance( "MD5" );
	md5.reset();
	md5.update( str.getBytes() );
	byte[] result = md5.digest();

	/* output */
	StringBuffer hexString = new StringBuffer();
	for ( int i = 0; i < result.length; i++ )
	{
	    if ( result[i] <= 15 && result[i] >= 0 )
	    {
		hexString.append( ZERO );
	    }
	    hexString.append( Integer.toHexString( 0xFF & result[i] ) );
	}
	return hexString.toString();
    }

    /**
     * creates an new HttpURLConnection-object and also sends the query, you only have to read the inputStream of the
     * return urlConn
     * 
     * @param message
     *            the message which is sent to the given url
     * @param url
     *            the url where the message is sent to
     * @return a new HttpURLConnection-object
     * @throws IOException
     *             thrown when a IOExceptions occurs
     */
    private static HttpURLConnection createURLConn( String message, URL url ) throws IOException
    {
	HttpURLConnection urlConn = (HttpURLConnection)url.openConnection();
	urlConn.setRequestMethod( "POST" );
	urlConn.setDoInput( true );
	urlConn.setDoOutput( true );
	urlConn.setUseCaches( false );
	urlConn.setRequestProperty( "User-Agent",
		"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2" );
	// send query
	OutputStreamWriter writer = new OutputStreamWriter( urlConn.getOutputStream() );
	writer.write( message );
	writer.close();

	return urlConn;
    }
}
